/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package fr.loof.fonzie.mock;

import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;

import java.lang.reflect.Field;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Query;

import org.easymock.EasyMock;
import org.springframework.util.ReflectionUtils;

import fr.loof.fonzie.dialects.JPADialectSupport;


/**
 * @author <a href="nicolas@apache.org">Nicolas De Loof</a>
 *
 */
public class EntityManagerMock
    implements EntityManager
{
    private EntityManager mock;

    public static final int ANYTIME = -1;

    public EntityManagerMock()
    {
        mock = createMock( EntityManager.class );
    }

    public EntityManagerMock queryFor( Object result )
    {
        return queryFor( result, 1 );
    }

    public EntityManagerMock queryFor( Object result, int time )
    {
        Query query = new QueryMock( result );
        expect( mock.createQuery( (String) anyObject() ) ).andReturn( query );
        if ( time > 1 )
        {
            expectLastCall().times( time );
        }
        if ( time == ANYTIME )
        {
            expectLastCall().anyTimes();
        }
        return this;
    }

    public void replay()
    {
        EasyMock.replay( mock );
    }

    public void clear()
    {
     // Nop
    }

    public void close()
    {
        // Nop
    }

    public boolean contains( Object entity )
    {
        throw new UnsupportedOperationException();
    }

    public Query createNamedQuery( String name )
    {
        return mock.createQuery( name );
    }

    public Query createNativeQuery( String sqlString, Class resultClass )
    {
        return mock.createQuery( sqlString );
    }

    public Query createNativeQuery( String sqlString, String resultSetMapping )
    {
        return mock.createQuery( sqlString );
    }

    public Query createNativeQuery( String sqlString )
    {
        return mock.createQuery( sqlString );
    }

    public Query createQuery( String qlString )
    {
        return mock.createQuery( qlString );
    }

    public <T> T find( Class<T> entityClass, Object primaryKey )
    {
        return (T) mock.createQuery( "" ).getSingleResult();
    }

    public void flush()
    {
     // Nop
    }

    public Object getDelegate()
    {
        return null;
    }

    public FlushModeType getFlushMode()
    {
        throw new UnsupportedOperationException();
    }

    public <T> T getReference( Class<T> entityClass, Object primaryKey )
    {
        try
        {
            T entity = (T) entityClass.newInstance();
            setId( primaryKey, entity );
            return entity;
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( "Impossible d'instancier " + entityClass );
        }
    }

    static void setId( Object id, Object entity )
    {
        Field f = JPADialectSupport.getIdField( entity );
        ReflectionUtils.setField( f, entity, id );
    }

    public EntityTransaction getTransaction()
    {
        return null;
    }

    public boolean isOpen()
    {
        return true;
    }

    public void joinTransaction()
    {
     // Nop
    }

    public void lock( Object entity, LockModeType lockMode )
    {
        // Nop
    }

    public <T> T merge( T entity )
    {
        return entity;
    }

    public void persist( Object entity )
    {
     // Nop
    }

    public void refresh( Object entity )
    {
     // Nop
    }

    public void remove( Object entity )
    {
     // Nop
    }

    public void setFlushMode( FlushModeType flushMode )
    {
     // Nop
    }
}
